package z1tryout

import (
	"context"
	"crypto/tls"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"os/exec"
	"runtime"
	"strings"
	"time"

	"gitee.com/z1gotool/deviceid"
	"gitee.com/z1gotool/z1err"
)

var TryOutDeadline = "2021-12-28 22:01:35"
var CheckUrlPre = ``
var fixCpuId = ``
var noAuthCpuId = `myzero1`
var slash = `myzeor1零壹`

func SetTryout(setting ...string) {
	if len(setting) > 0 {
		CheckUrlPre = setting[0]
	}

	if len(setting) > 1 {
		TryOutDeadline = setting[1]
	}

	if len(setting) > 2 {
		fixCpuId = setting[2]
	}

	if len(setting) > 3 {
		noAuthCpuId = setting[3]
	}
}

func TryoutExpired() (retCanUse bool) {
	isVm, err := IsVirtualMachine()
	z1err.Check(err)
	if isVm {
		return false
	}

	if fixCpuId == `` {
		tmp, err := GetCPUID()
		// log.Println(tmp)
		tmp = DeviceID(tmp)
		fixCpuId = tmp
		z1err.Check(err)
		if fixCpuId == `` {
			return false
		}
	}

	// for local
	if CheckUrlPre == `` {

		if noAuthCpuId == fixCpuId {
			return true
		} else {
			return false
		}
	}

	deadline1, err := time.ParseInLocation(`2006-01-02 15:04:05`, TryOutDeadline, time.Local)
	z1err.Check(err)
	if time.Now().Unix() <= deadline1.Unix() {
		return true
	}

	// https://gitee.com/myzero1/tryout/raw/master/test/BFEBFBFF000906EA
	url := fmt.Sprintf(`%s%s`, CheckUrlPre, fixCpuId)
	// resp, err := http.Get(url)
	resp, err := getRemoteInfo(url)
	z1err.Check(err)
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return false
	}

	body, err := io.ReadAll(resp.Body)
	z1err.Check(err)
	bodystr := string(body)
	bodystr = strings.Trim(bodystr, ` `)
	deadline, err := time.ParseInLocation(`2006-01-02 15:04:05`, bodystr, time.Local)
	z1err.Check(err)

	remoteDate := resp.Header["Date"][0]
	remoteDate = strings.Split(remoteDate, `, `)[1]
	remoteTime, err := time.ParseInLocation(`02 Jan 2006 15:04:05 GMT`, remoteDate, time.Local)
	remoteTime = remoteTime.Add(8 * time.Hour)
	z1err.Check(err)

	if remoteTime.Unix() <= deadline.Unix() {
		return true
	}

	return
}

func IsVirtualMachine() (retIsVM bool, retErr error) {
	defer z1err.Handle(&retErr)

	// https://www.dazhuanlan.com/fangzhou422/topics/1681308
	model := ""
	var cmd *exec.Cmd

	if runtime.GOOS == "windows" {
		cmd = exec.Command("cmd", "/C", "wmic path Win32_ComputerSystem get Model")
	} else { // linux
		cmd = exec.Command("/bin/bash", "-c", "dmidecode | egrep -i 'system-product-name|product|domU'")
	}

	stdout, err := cmd.Output()
	z1err.Check(err)

	model = string(stdout)

	if strings.Contains(model, "VirtualBox") || strings.Contains(model, "Virtual Machine") || strings.Contains(model, "VMware Virtual Platform") ||
		strings.Contains(model, "KVM") || strings.Contains(model, "Bochs") || strings.Contains(model, "HVM domU") {
		retIsVM = true
	}

	return
}

// GetCPUID 获取cpuid
func GetCPUID() (retCpuid string, retErr error) {
	defer z1err.Handle(&retErr)

	// https://blog.dandelioncloud.cn/article/details/1601867975835729921

	// https://www.cnblogs.com/wuyaxiansheng/p/13576174.html
	cmd := exec.Command("wmic", "cpu", "get", "processorid")
	b, err := cmd.CombinedOutput()
	z1err.Check(err)

	cpuid := string(b)
	cpuid = cpuid[12 : len(cpuid)-2]
	cpuid = strings.ReplaceAll(cpuid, "\n", "")
	cpuid = strings.ReplaceAll(cpuid, "\r", "")
	cpuid = strings.ReplaceAll(cpuid, " ", "")

	retCpuid = cpuid

	return
}

// GetBaseBoardID 获取主板的id
func GetBaseBoardID() (retBoardID string, retErr error) {
	defer z1err.Handle(&retErr)

	cmd := exec.Command("wmic", "baseboard", "get", "serialnumber")
	b, err := cmd.CombinedOutput()
	z1err.Check(err)

	cpuid := string(b)
	cpuid = cpuid[12 : len(cpuid)-2]
	cpuid = strings.ReplaceAll(cpuid, "\n", "")
	retBoardID = cpuid

	return
}

func getRemoteInfo(url string) (resp *http.Response, err error) {
	defer z1err.Handle(&err)

	// https://gitee.com/myzero1/tryout/raw/master/test/BFEBFBFF000906EA
	// https://zhidao.baidu.com/question/994067537503938939.html
	var (
		// dnsResolverIP = "8.8.8.8:53" // Google DNS resolver.
		// dnsResolverIP = "114.114.114.114:53" // Google DNS resolver.
		dnsResolverIP        = "223.5.5.5:53" // Google DNS resolver.
		dnsResolverProto     = "udp"          // Protocol to use for the DNS resolver
		dnsResolverTimeoutMs = 5000           // Timeout (ms) for the DNS resolver (optional)
	)

	dialer := &net.Dialer{
		Resolver: &net.Resolver{
			PreferGo: true,
			Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
				d := net.Dialer{
					Timeout: time.Duration(dnsResolverTimeoutMs) * time.Millisecond,
				}
				return d.DialContext(ctx, dnsResolverProto, dnsResolverIP)
			},
		},
	}

	dialContext := func(ctx context.Context, network, addr string) (net.Conn, error) {
		return dialer.DialContext(ctx, network, addr)
	}

	http.DefaultTransport.(*http.Transport).DialContext = dialContext
	http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	httpClient := &http.Client{}

	// Testing the new HTTP client with the custom DNS resolver.
	resp, err = httpClient.Get(url)
	// z1err.Check(err)

	// defer resp.Body.Close()
	// body, err := io.ReadAll(resp.Body)
	// z1err.Check(err)

	// content = string(body)

	return
}

var DeviceIDKey = `myzero1`

func DeviceID(input string) (out string) {
	retCpuid, err := deviceid.GetId(DeviceIDKey)
	z1err.Check(err)

	out = retCpuid

	return
}

func ShowDeviceID() {
	retCpuid, err := deviceid.GetId(DeviceIDKey)
	z1err.Check(err)
	log.Println(`========== ShowDeviceID,start ==========`)
	log.Println(`----`, retCpuid)
	log.Println(`========== ShowDeviceID,end   ==========`)
}
